package {

    import flexunit.framework.TestCase;

    import mx.collections.ArrayCollection;
    import mx.collections.Sort;
    import mx.collections.SortField;
    import mx.events.CollectionEvent;
    import mx.events.CollectionEventKind;

    public class ArrayCollectionHelperTest extends TestCase {

        public function getSource():ArrayCollection {
            return new ArrayCollection(["A", "B", "C"]);
        }

        public function test_runtimeSourceTest():void {
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper();
            var source:ArrayCollection = getSource();
            helper.dataProvider = source;

            // source and helper ["A", "B", "C"]
            assertEquals(source.length, helper.length);
        }

        public function test_constructorSourceTest():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper(source);

            // source and helper ["A", "B", "C"]
            assertEquals(source.length, helper.length);
        }

        public function test_defaultAddItem():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper(source);
            source.addItem("D");
            source.addItemAt("3", 0);
            source.addItemAt("2", 0);
            source.addItemAt("1", 0);

            // source and helper [1, 2, 3, "A", "B", "C", "D"]
            for (var i:int = 0; i < source.length; i++) {
                assertEquals(source.getItemAt(i), helper.getItemAt(i));
            }
        }

        public function test_customAddItem():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper();
            helper.addUpdateItemFunction = function(item:Object, index:uint):Object {
                return {name:item};
            };
            helper.dataProvider = source;

            source.addItem("D");
            source.addItemAt("3", 0);
            source.addItemAt("2", 0);
            source.addItemAt("1", 0);

            // source [1, 2, 3, "A", "B", "C", "D"]
            // helper [{name:1}, {name:2}, {name:3}, {name:"A"} ...]
            for (var i:int = 0; i < source.length; i++) {
                // Items not equals
                assertFalse(source.getItemAt(i) == helper.getItemAt(i));
            }

            for (i = 0; i < source.length; i++) {
                // Now should be OK
                assertTrue(source.getItemAt(i) == helper.getItemAt(i).name);
            }
        }

        public function test_defaultUpdateItem():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper(source);

            source.setItemAt("Z", 1);

            // source and helper ["A", "Z", "C"]
            assertEquals(source.getItemAt(1), helper.getItemAt(1));
        }

        public function test_updateItemWithCustomUpdate():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper();
            helper.addUpdateItemFunction = function(item:Object, index:uint):Object {
                return {name:item};
            };
            helper.dataProvider = source;

            source.setItemAt("Z", 1);

            // source ["A", "Z", "C"]
            // helper [{name:"A"}, {name:"Z"}, {name:"C"}]

            // Should not be match
            assertFalse(source.getItemAt(1) == helper.getItemAt(1));
            // Update should be work here
            assertTrue(source.getItemAt(1) == helper.getItemAt(1).name);
        }

        public function test_defaultRemoveItem():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper(source);

            source.removeItemAt(1);

            // source and helper ["A", "C"]
            assertEquals(source.length, helper.length);
        }

        public function test_removeItemWithCustomAdd():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper();
            helper.addUpdateItemFunction = function(item:Object, index:uint):Object {
                return {name:item};
            };
            helper.dataProvider = source;

            source.removeItemAt(1);

            // source ["A", "C"]
            // helper [{name:"A"}, {name:"C"}]
            assertTrue(source.length == helper.length);
        }

        public function test_resetSource():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper(source);

            source.list = new ArrayCollection([1, 2, 3, 4, 5]);

            // source and helper [1, 2, 3, 4, 5]
            assertEquals(source.length, helper.length);
        }

        public function test_collectionEventHandler():void {
            var gotKind:String = null;
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper();
            helper.collectionChangeFunction = function(event:CollectionEvent):void {
                // We must get unique event kind (without repeating)
                assertFalse(gotKind == event.kind);
                gotKind = event.kind;
            };

            helper.dataProvider = source;

            // source and helper ["A", "B", "C"]
            assertEquals(source.length, helper.length);

            // but we should not get any kind(s)
            assertEquals(gotKind, null);

            source.addItem("E");
            // source and helper ["A", "B", "C", "E"]
            assertEquals(gotKind, CollectionEventKind.ADD);

            source.setItemAt("D", source.length - 1);
            // source and helper ["A", "B", "C", "D"]
            assertEquals(gotKind, CollectionEventKind.REPLACE);

            source.removeItemAt(source.length - 1);
            // source and helper ["A", "B", "C"]
            assertEquals(gotKind, CollectionEventKind.REMOVE);

            source.list = new ArrayCollection([1, 2, 3, 4, 5]);
            // source and helper [1, 2, 3, 4, 5]
            assertEquals(gotKind, CollectionEventKind.RESET);
        }

        public function test_sorting():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper();

            var sort:Sort = new Sort();
            sort.fields = [new SortField(null, true /*caseInsensitive*/, true /*descending*/)];
            helper.sort = sort;
            helper.refresh();

            source.addItem("D");

            // source ["A", "B", "C", "D"]
            // helper ["D", "C", "B", "A"]
            for(var i:uint = 0; i < helper.length; i++) {
                assertEquals(helper.getItemAt(i), source.getItemAt(source.length - 1 - i));
            }
        }

        public function test_filtering():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper(source);

            helper.filterFunction = function(item:Object):Boolean {
                return item != "B";
            };
            helper.refresh();

            source.addItem("D");

            // source ["A", "B", "C", "D"]
            // helper ["A", "C", "D"]
            assertEquals(source.length - 1, helper.length);
        }

        /**
         *  Testing changes in the target ArrayCollection
         *
         *  Validate changes in the target instance we are updating properly a source
         */

        public function test_list():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper();
            helper.addUpdateItemFunction = function(item:Object, index:uint):Object {
                return {name:item};
            };

            var sort:Sort = new Sort();
            sort.fields = [new SortField("name", true /*caseInsensitive*/, true /*descending*/)];
            helper.sort = sort;

            // source ["A", "B", "C"]
            // helper [{name:"C"}, {name:"B"}, {name:"A"}]
            helper.dataProvider = source;

            helper.setList(new ArrayCollection([1, 2, 3, 4, 5]));

            // source [1, 2, 3, 4, 5]
            // helper [{name:5}, {name:4}, {name:3}, {name:2}, {name:1}]
            for (var i:int = 0; i < source.length; i++) {
                assertEquals(source.getItemAt(i), helper.getItemAt(source.length - 1 - i).name);
            }
        }

        public function test_filterAddItem():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper(source);

            helper.filterFunction = function(item:Object):Boolean {
                return item != "B";
            };
            helper.refresh();

            helper.addItem("E");
            helper.addItemAt("D", 2);

            // source ["A", "B", "C", "D", "E"]
            // helper ["A", "C", "D", "E"]
            assertEquals(source.getItemAt(source.length - 1), helper.getItemAt(helper.length - 1));
            assertEquals(source.length - 1, helper.length);
        }

        public function test_filterSetItem():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper(source);

            helper.filterFunction = function(item:Object):Boolean {
                return item != "B";
            };
            helper.refresh();

            helper.addItem("E");
            helper.setItemAt("D", helper.length - 1);

            // source ["A", "B", "C", "D"]
            // helper ["A", "C", "D"]
            assertEquals(source.getItemAt(source.length - 1), helper.getItemAt(helper.length - 1));
            assertEquals(source.length - 1, helper.length);
        }

        public function test_filterRemoveItem():void {
            var source:ArrayCollection = getSource();
            var helper:ArrayCollectionHelper = new ArrayCollectionHelper(source);

            helper.filterFunction = function(item:Object):Boolean {
                return item != "B";
            };
            helper.refresh();

            helper.addItem("D");
            helper.removeItemAt(1);

            // source ["A", "B", "D"]
            // helper ["A", "D"]
            assertEquals(source.length - 1, helper.length);
        }
    }
}